package com.meizhao.api.configuration;

import cn.hutool.db.Db;
import cn.hutool.db.Session;
import com.meizhao.api.interceptor.AllInterceptor;
import com.meizhao.api.interceptor.AuthInterceptor;
import com.meizhao.api.utils.HttpUtils;
import com.meizhao.api.utils.VisiableThreadPoolTaskExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ThreadPoolExecutor;


@Slf4j
@Configuration
@EnableWebMvc
@EnableCaching
@EnableAsync
@EnableScheduling
public class SystemConfig implements WebMvcConfigurer {

    private ConfigProperties configProperties;

	private AllInterceptor allInterceptor;

	private AuthInterceptor authInterceptor;

	private Environment environment;

    private PlatformTransactionManager transactionManager;

    @Autowired
    public SystemConfig(
            AllInterceptor allInterceptor,
            AuthInterceptor authInterceptor,
            ConfigProperties configProperties,
            PlatformTransactionManager transactionManager,
            Environment environment){
        this.allInterceptor = allInterceptor;
        this.authInterceptor = authInterceptor;
        this.configProperties = configProperties;
        this.transactionManager = transactionManager;
        this.environment = environment;
    }


    @Bean
    public Db db(DataSource dataSource){
        return Db.use(dataSource);
    }

    @Bean
    public Session session(DataSource dataSource){
        return Session.create(dataSource);
    }

    @Override
    public void addViewControllers( ViewControllerRegistry registry ) {

        registry.addRedirectViewController("/", configProperties.getNoLoginUrl());
        registry.setOrder( Ordered.HIGHEST_PRECEDENCE );
    }


    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedMethods("*")
                .allowedOrigins("*")
                .allowedHeaders("*")
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
                .maxAge(3600 * 24);
    }


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        
        /*registry.addResourceHandler("/js/**").addResourceLocations("classpath:/static/v1.0/js/").resourceChain(true);
        registry.addResourceHandler("/css/**").addResourceLocations("classpath:/static/v1.0/css/").resourceChain(true);
        registry.addResourceHandler("/images/**").addResourceLocations("classpath:/static/v1.0/images/").resourceChain(true);
        registry.addResourceHandler("/fonts/**").addResourceLocations("classpath:/static/v1.0/fonts/").resourceChain(true);
        registry.addResourceHandler("/lib/layui/**").addResourceLocations("classpath:/static/v1.0/lib/layui/").resourceChain(true);*/

        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

        if(environment.getProperty("os.name").contains("Windows")) {
            registry.addResourceHandler("/"+configProperties.getFilePattern()+"**").addResourceLocations("file:"+configProperties.getFileWinPath());
            registry.addResourceHandler("/"+configProperties.getAvatarPattern()+"**").addResourceLocations("file:"+configProperties.getFileWinAvatarPath());
        }else{
            registry.addResourceHandler("/"+configProperties.getFilePattern()+"**").addResourceLocations("file:"+configProperties.getFileLinuxPath());
            registry.addResourceHandler("/"+configProperties.getAvatarPattern()+"**").addResourceLocations("file:"+configProperties.getFileLinuxAvatarPath());
        }
    }
	
	
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}

	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		
		registry.addInterceptor(allInterceptor).addPathPatterns("/**");
        registry.addInterceptor(authInterceptor).addPathPatterns("/**");
		registry.addInterceptor(localeChangeInterceptor());
	}

    @Bean
    public LocaleResolver localeResolver(){

        return new NativeLocaleResolver();
    }

    protected static class NativeLocaleResolver implements LocaleResolver{

        public Locale resolveLocale(HttpServletRequest request) {
            String language = request.getParameter("lang");
            Locale locale = Locale.getDefault();
            if(!StringUtils.isEmpty(language)){
                String[] split = language.split("_");
                locale = new Locale(split[0],split[1]);
            }
            return locale;
        }

        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

        }
    }

    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
        lci.setParamName("lang");
        return lci;
    }


    @Bean
    public HttpMessageConverter<String> responseBodyConverter() {
        return new StringHttpMessageConverter(Charset.forName("UTF-8"));
    }

    private MappingJackson2HttpMessageConverter messageConverter() {
        MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(HttpUtils.getObjectMapper());
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
        supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
        supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        return converter;
    }


    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //解决中文乱码
        converters.add(responseBodyConverter());
        converters.add(messageConverter());
    }

    @Primary
    @Bean("messageSource")
    public MessageSource initMessageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:i18n/messages");
        return messageSource;
    }


    /*@Bean(name = "txAdvice")
    public TransactionInterceptor getAdvisor() {

        DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
        txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
        txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        txAttr_REQUIRED_READONLY.setReadOnly(true);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        source.addTransactionalMethod("save*", txAttr_REQUIRED);
        source.addTransactionalMethod("delete*", txAttr_REQUIRED);
        source.addTransactionalMethod("update*", txAttr_REQUIRED);
        source.addTransactionalMethod("exec*", txAttr_REQUIRED);
        source.addTransactionalMethod("set*", txAttr_REQUIRED);
        source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
        source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);

        return new TransactionInterceptor((TransactionManager)transactionManager, source);
    }

    @Bean
    public BeanNameAutoProxyCreator txProxy() {
        BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
        creator.setInterceptorNames("txAdvice");
        creator.setBeanNames("*Service", "*ServiceImpl");
        creator.setProxyTargetClass(true);
        return creator;
    }*/

    @Bean
    public ModelMapper modelMapper(){
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setFullTypeMatchingRequired(true);
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        return modelMapper;
	}
   
    @Bean("threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        
    	ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        executor.setCorePoolSize(50);
        executor.setMaxPoolSize(500);
        executor.setQueueCapacity(999);
        executor.setThreadNamePrefix("async-service-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        
        log.info("start asyncServiceExecutor...");
        return executor;
    }

}
